package tcp.controller;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Alert;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.stage.FileChooser;
import javafx.stage.Stage;

import java.io.*;
import java.net.Socket;
import java.net.SocketException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.ResourceBundle;

public class ClintAController implements Initializable {

    @FXML
    AnchorPane rootLayout;
    @FXML
    TextField server_ip;
    @FXML
    TextField server_port;
    @FXML
    TextArea plainText;
    @FXML
    TextArea messageContent;

    // 网络编程相关变量
    private Socket socket = null;
    private OutputStream out = null;
    private DataOutputStream dos = null;
    private InputStream in = null;
    private DataInputStream dis = null;
    private boolean flag = false;

    // 点击【连接】按钮
    public void connectServer() {
        String ip = server_ip.getText();
        Integer port = Integer.valueOf(server_port.getText());
        // 建立连接, 需要提供 主机号 和 端口号
        connect(ip, port);
        new Thread(new ReceiveMessage()).start();
    }

    // 点击【解密密文】按钮
    public void decrypt() {
        System.out.println("解密");
        sendMessageToServer("jiemi");
    }

    // 点击【选择文件进行加密】
    public void chooseFile() throws Exception {
        FileChooser fileChooser = new FileChooser();
        fileChooser.setTitle("选择文件");
        Stage stage =  (Stage) rootLayout.getScene().getWindow();
        File file = fileChooser.showOpenDialog(stage);
        String filePath = file + "";
//        String time = new SimpleDateFormat("YYYY-mm-dd hh:MM:ss").format(new Date());
//        messageContent.appendText(time + "Alice发送的数据为："+ message + "\n");

        String message = "";
        String separator = File.separator;
        String replace = filePath.replaceAll("\\\\", "/");
        {
            int bufSize = 10240;
            byte[] bs = new byte[bufSize];
            // 这里是分配缓存大小。也就是用来存放从硬盘中度出来的文件
            // 什么叫一次把文件读出来？其实就是当缓存大小和在硬盘中文件大小一样，
            // 只通过一个read指令把整个文件都扔到缓存里面。例如要一次读一个2G的文件，把缓存设为2G就能一次读出来。
            // 不过当分配空间的时候，这个缓存根本是分配不出来的，因为内存不足。
            ByteBuffer byteBuf = ByteBuffer.allocate(bufSize);
            FileChannel channel = new RandomAccessFile(replace, "r").getChannel();
            int size;
            // 因为这里缓存大小是1K，所以每个channel.read()指令最多只会读到文件的1K的内容。
            // 如果文件有1M大小，这里for会循环1024次，把文件分开1024次读取出来
            while ((size = channel.read(byteBuf)) != -1) {
                byteBuf.rewind();
                byteBuf.get(bs);
                // 把文件当字符串处理，打印。
                String str = new String(bs, 0, size);
                message = message + str;
                System.out.println(message);
                byteBuf.clear();
            }
            channel.close();
        }
        sendMessageToServer(message);
    }

    // 点击【发送加密文件】
    public void fileEncrypt() throws Exception {
        FileChooser fileChooser = new FileChooser();
        fileChooser.setTitle("选择文件");
        Stage stage =  (Stage) rootLayout.getScene().getWindow();
        File file = fileChooser.showOpenDialog(stage);
        String filePath = file + "";
//        String time = new SimpleDateFormat("YYYY-mm-dd hh:MM:ss").format(new Date());
//        messageContent.appendText(time + "Alice发送的数据为："+ message + "\n");

        String message = "jiami";
        String separator = File.separator;
        String replace = filePath.replaceAll("\\\\", "/");
        {
            int bufSize = 10240;
            byte[] bs = new byte[bufSize];
            // 这里是分配缓存大小。也就是用来存放从硬盘中度出来的文件
            // 什么叫一次把文件读出来？其实就是当缓存大小和在硬盘中文件大小一样，
            // 只通过一个read指令把整个文件都扔到缓存里面。例如要一次读一个2G的文件，把缓存设为2G就能一次读出来。
            // 不过当分配空间的时候，这个缓存根本是分配不出来的，因为内存不足。
            ByteBuffer byteBuf = ByteBuffer.allocate(bufSize);
            FileChannel channel = new RandomAccessFile(replace, "r").getChannel();
            int size;
            // 因为这里缓存大小是1K，所以每个channel.read()指令最多只会读到文件的1K的内容。
            // 如果文件有1M大小，这里for会循环1024次，把文件分开1024次读取出来
            while ((size = channel.read(byteBuf)) != -1) {
                byteBuf.rewind();
                byteBuf.get(bs);
                // 把文件当字符串处理，打印。
                String str = new String(bs, 0, size);
                message = message + str;
                System.out.println(message);
                byteBuf.clear();
            }
            channel.close();
        }
        sendMessageToServer(message);
    }

    // 点击【向B发送明文】按钮
    public void sendPlainText() {
        System.out.println("发送明文!");
        // 记录当前时间作为发送时间
//        String time = new SimpleDateFormat("YYYY-mm-dd hh:MM:ss").format(new Date());
        String message = plainText.getText();
//        messageContent.appendText(time + "Alice发送的数据为："+ message + "\n");
        sendMessageToServer(message);
    }

    // 点击【向B发送密文】按钮
    public void sendCipherText() {
        System.out.println("加密!");
//        String time = new SimpleDateFormat("YYYY-mm-dd hh:MM:ss").format(new Date());
        String message = "jiami" + plainText.getText();
//        messageContent.appendText(time + "Alice发送的数据为："+ message + "\n");
        sendMessageToServer(message);
    }

    // 点击【退出】按钮
    public void exit() {
        disconnect(); // 断开连接
        System.out.println("连接关闭成功!");
        System.exit(0); // 关闭系统
    }

    // 点击【清空】按钮
    public void clear() {
        messageContent.clear();
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        server_ip.setText("127.0.0.1");
        server_port.setText("8888");
    }

    // ReceiveMessage类 - 使用多线程封装接收端
    private class ReceiveMessage implements Runnable {
        @Override
        public void run() {
            flag = true;
            try {
                messageContent.setText("");
                while (flag) {
                    String message = dis.readUTF(); // 读取服务器的消息
                    messageContent.appendText(message + "\n");
                    System.out.println(message); // 2021-40-03 12:06:32-IP地址：-/127.0.0.1--用户B O b: 321
                }
            } catch (EOFException | SocketException e) {
                flag = false;
                System.out.println("客户端已关闭");
                 e.printStackTrace();
            } catch (IOException e) {
                flag = false;
                System.out.println("接受消息失败");
                e.printStackTrace();
            }
        }

    }

    // 连接服务器
    private void connect(String host, int port) {
        try {
            // 1.创建客户端socket，指定服务器地址和端口
            socket = new Socket(host, port);
            // 2.获取输出流，向服务器端发送消息
            out = socket.getOutputStream(); // 字节输出流
            dos = new DataOutputStream(out); // 将文件流包装成数据流
            in = socket.getInputStream();
            dis = new DataInputStream(in);
            alert("连接成功", null, "成功连接上IP:" + host + ", 端口:" + port + "的服务器");
        } catch (IOException e) {
            System.out.println("申请链接失败");
            alert("连接失败", null, "未能连接到IP:"  + host + ", 端口:" + port + "的服务器");
            e.printStackTrace();
        }
    }

    // 与服务器断开连接
    private void disconnect() {
        flag = false;
        if (dos != null) {
            try {
                dos.close();
            } catch (IOException e) {
                System.out.println("dos关闭失败");
                e.printStackTrace();
            }
        }
        if (out != null) {
            try {
                out.close();
            } catch (IOException e) {
                System.out.println("dos关闭失败");
                e.printStackTrace();
            }
        }
        if (socket != null) {
            try {
                socket.close();
            } catch (IOException e) {
                System.out.println("socket关闭失败");
                e.printStackTrace();
            }
        }
    }

    // 往服务器发送消息
    private void sendMessageToServer(String message) {
        try {
            dos.writeUTF("用户Alice: " + message);
            dos.flush();
        } catch (IOException e) {
            System.out.println("发送消息失败");
            e.printStackTrace();
        }
    }

    // 弹框方法封装
    public void alert(String title, String headerText, String contentText) {
        Alert alert = new Alert(Alert.AlertType.INFORMATION);
        alert.setTitle(title);
        alert.setHeaderText(headerText);
        alert.setContentText(contentText);
        alert.showAndWait();
    }
}
